package com.qipay.admin.statement.service;

import com.qipay.admin.order.vo.OrderVo;
import com.qipay.admin.statement.vo.DataSetVo;
import com.qipay.admin.statement.vo.WithdrawResultVo;
import com.qipay.admin.utils.SiteHelper;
import com.qipay.baas.model.*;
import com.qipay.cp.CpService;
import com.qipay.cp.config.CpAccountStateEnum;
import com.qipay.cpaccount.CpAccountTradLogService;
import com.qipay.cpinfo.service.CpInfoService;
import com.qipay.excel.service.ExcelService;
import com.qipay.excel.vo.Cell;
import com.qipay.order.config.OrderChannelEnum;
import com.qipay.order.service.OrderService;
import com.qipay.order.vo.OrderSpStatementVo;
import com.qipay.order.vo.OrderStatementVo;
import com.qipay.qipay.QipayTradlogService;
import com.qipay.security.UserDetailsServiceImpl;
import com.qipay.sp.SpService;
import com.qipay.user.entity.UserLevel;
import com.qipay.utils.DateUtils;
import com.qipay.withdraw.config.WithdrawStateEnum;
import com.qipay.withdraw.config.WithdrawTypeEnum;
import com.qipay.withdraw.service.WithdrawService;
import io.jsonwebtoken.lang.Assert;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ModelAndView;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Creator: Yao
 * Date:    2017/11/12
 * For:
 * Other:
 */
@Service
public class StatementService {

    @Autowired
    QipayTradlogService qipayTradlogService;
    @Autowired
    CpAccountTradLogService cpTradLogService;
    @Autowired
    CpService cpService;
    @Autowired
    CpInfoService cpInfoService;
    @Autowired
    OrderService orderService;
    @Autowired
    ExcelService excelService;
    @Autowired
    SpService spService;
    @Autowired
    WithdrawService withdrawService;

    private static final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");

    // Views

    public void fillView(ModelAndView view, UserDetailsServiceImpl.LoginUserDetails loginUser, Long cpId, String from, String to) throws ParseException {

        // init
        List<Long> cpIdList = null;
        List<String> labels = new ArrayList<>();
        List<DataSetVo> datas = new ArrayList<>();
        UserLevel level = loginUser.getLevel();
        Calendar calendar = Calendar.getInstance();
        Long curCpId = null;

        // params
        to = DateUtils.checkElseNow(to);
        if (StringUtils.isNotBlank(from)) {
            Assert.isTrue(from.matches(DateUtils.DATE_PATTERN), "指定开始日期格式有误，非 YYYY-MM-DD 形式");
        } else {
            calendar.add(Calendar.MONTH, -1);
            from = DateUtils.FORMAT_YYYY_MM_DD.format(calendar.getTime());
        }

        // query + convert
        if (level.equals(UserLevel.ADMIN) && null == cpId) {

            List<QipayTradlog> tradlogs = qipayTradlogService.list(from, to);
            fill(labels, datas, from, to, tradlogs);
        } else {

            // cp.current
            curCpId = null != cpId ? cpId : Long.valueOf(loginUser.getUsername());
            Assert.notNull(curCpId, "未指定查询 CP.ID 信息");
            cpIdList = new ArrayList<>();
            cpIdList.add(curCpId);

            // cp.agent
            List<Cp> cpList = cpService.findByAgentIdAndAccountState(curCpId, CpAccountStateEnum.ENABLED.getState());
            if (CollectionUtils.isNotEmpty(cpList)) {
                cpIdList.addAll(cpList.stream().map(p -> p.getId()).collect(Collectors.toList()));
            }

            // filter
            List<CpAccountTradlog> tradlogs = cpTradLogService.list(cpIdList, from, to);
            fillCp(labels, datas, cpIdList, from, to, tradlogs);
        }

        // fill
        view.addObject("labels", labels);
        view.addObject("data", datas);

        // params
        view.addObject("from", from);
        view.addObject("to", to);
        view.addObject("cpId", cpId);
        fillCpList(view, level, loginUser);
    }

    public void fillOrderView(ModelAndView view, Long cpId, String from, String to, Integer channel) throws ParseException {

        // check
        Assert.notNull(view);
        Assert.notNull(cpId);

        // params
        to = DateUtils.checkElseNow(to);
        if (StringUtils.isNotBlank(from)) {
            Assert.isTrue(from.matches(DateUtils.DATE_PATTERN), "指定开始日期格式有误，非 YYYY-MM-DD 形式");
        } else {
            from = to;
        }

        // calc
        calcOrderView(view, cpId, from, to, channel);
        // params

        view.addObject("channels", OrderChannelEnum.map());
        view.addObject("from", from);
        view.addObject("to", to);
        view.addObject("channel", channel);
        view.addObject("cpId", cpId);
    }

    public void fillAllView(ModelAndView view, String from, String to, Long cpId, String cpName, Long agentId, String contract, Integer channel) throws ParseException {

        // check
        Assert.notNull(view);

        // params
        to = DateUtils.checkElseNow(to);
        if (StringUtils.isNotBlank(from)) {
            Assert.isTrue(from.matches(DateUtils.DATE_PATTERN), "指定开始日期格式有误，非 YYYY-MM-DD 形式");
        } else {
            from = to;
        }

        // params
        view.addObject("lst", calcAllView(view, from, to, cpId, cpName, agentId, contract, channel));
        view.addObject("withdrawLst", calcWithdrawView(view, from, to));
        view.addObject("channels", OrderChannelEnum.map());

        view.addObject("from", from);
        view.addObject("to", to);
        view.addObject("cpId", cpId);
        view.addObject("cpName", cpName);
        view.addObject("agentId", agentId);
        view.addObject("contract", contract);
        view.addObject("channel", channel);
    }

    public void fillSpView(ModelAndView view, String from, String to, Long spId, String spName, String service, Integer channel) throws ParseException {

        // check
        Assert.notNull(view);

        // init
        to = DateUtils.checkElseNow(to);
        if (StringUtils.isNotBlank(from)) {
            Assert.isTrue(from.matches(DateUtils.DATE_PATTERN), "指定开始日期格式有误，非 YYYY-MM-DD 形式");
        } else {
            from = to;
        }

        // params
        view.addObject("lst", calcSpView(view, from, to, spId, spName, service, channel));
        view.addObject("channels", OrderChannelEnum.map());

        view.addObject("from", from);
        view.addObject("to", to);
        view.addObject("spId", spId);
        view.addObject("spName", spName);
        view.addObject("service", service);
        view.addObject("channel", channel);
    }

    // Export

    public HSSFWorkbook ExportOrderView(Long cpId, String from, String to, Integer channel) throws ParseException, IOException {

        // check
        Assert.notNull(cpId);

        // params
        to = DateUtils.checkElseNow(to);
        if (StringUtils.isNotBlank(from)) {
            Assert.isTrue(from.matches(DateUtils.DATE_PATTERN), "指定开始日期格式有误，非 YYYY-MM-DD 形式");
        } else {
            from = to;
        }

        // init
        List<Cell> list = new ArrayList<>();
        int rowIdx = 0;
        int cellIdx = 0;
        List<List<String>> orderList = calcOrderView(null, cpId, from, to, channel);

        // init.title
        list.add(new Cell(rowIdx, cellIdx++, "日期"));
        list.add(new Cell(rowIdx, cellIdx++, "交付类型"));
        list.add(new Cell(rowIdx, cellIdx++, "支付成功总笔数"));
        list.add(new Cell(rowIdx, cellIdx++, "支付成功总金额"));
        list.add(new Cell(rowIdx, cellIdx++, "商户费率"));
        list.add(new Cell(rowIdx, cellIdx++, "商户手续费（元）"));

        // init.content
        excelService.fill(list, orderList, rowIdx);

        return excelService.fill("", list);
    }

    public HSSFWorkbook ExportSpView(String from, String to, Long spId, String spName, String service, Integer channel) throws ParseException, IOException {

        // init.date
        to = DateUtils.checkElseNow(to);
        if (StringUtils.isNotBlank(from)) {
            Assert.isTrue(from.matches(DateUtils.DATE_PATTERN), "指定开始日期格式有误，非 YYYY-MM-DD 形式");
        } else {
            from = to;
        }

        // init.excel
        List<Cell> list = new ArrayList<>();
        int rowIdx = 0;
        int cellIdx = 0;
        List<List<String>> detailList = calcSpView(null, from, to, spId, spName, service, channel);

        // init.title
        list.add(new Cell(rowIdx, cellIdx++, "SP ID"));
        list.add(new Cell(rowIdx, cellIdx++, "渠道"));
        list.add(new Cell(rowIdx, cellIdx++, "SP 账号"));
        list.add(new Cell(rowIdx, cellIdx++, "SP 名称"));
        list.add(new Cell(rowIdx, cellIdx++, "支付方式"));
        list.add(new Cell(rowIdx, cellIdx++, "支付笔数"));
        list.add(new Cell(rowIdx, cellIdx++, "总金额（元）"));
        list.add(new Cell(rowIdx, cellIdx++, "SP 费率"));
        list.add(new Cell(rowIdx, cellIdx++, "SP 手续费（元）"));
        list.add(new Cell(rowIdx, cellIdx++, "划账金额（元）"));
        list.add(new Cell(rowIdx, cellIdx++, "银行卡号"));

        // init.content
        excelService.fill(list, detailList, rowIdx);

        return excelService.fill("", list);
    }

    public HSSFWorkbook ExportAllView(UserDetailsServiceImpl.LoginUserDetails loginUser, String from, String to, Long cpId, String cpName, Long agentId, String contract, Integer channel) throws ParseException, IOException {

        // init
        to = DateUtils.checkElseNow(to);
        if (StringUtils.isNotBlank(from)) {
            Assert.isTrue(from.matches(DateUtils.DATE_PATTERN), "指定开始日期格式有误，非 YYYY-MM-DD 形式");
        } else {
            from = to;
        }
        UserLevel level = loginUser.getLevel();

        // init.excel
        List<Cell> list = new ArrayList<>();
        int rowIdx = 0;
        int cellIdx = 0;
        List<List<String>> detailList = calcAllView(null, from, to, cpId, cpName, agentId, contract, channel);

        // init.title
        list.add(new Cell(rowIdx, cellIdx++, "日期"));
        list.add(new Cell(rowIdx, cellIdx++, "商户 ID"));
        list.add(new Cell(rowIdx, cellIdx++, "商户名称"));
        if (level.equals(UserLevel.ADMIN)) {
            list.add(new Cell(rowIdx, cellIdx++, "代理商 ID"));
            list.add(new Cell(rowIdx, cellIdx++, "代理商联系人"));
        }
        list.add(new Cell(rowIdx, cellIdx++, "交付类型"));
        list.add(new Cell(rowIdx, cellIdx++, "支付成功总笔数"));
        list.add(new Cell(rowIdx, cellIdx++, "支付成功总金额（元）"));
        list.add(new Cell(rowIdx, cellIdx++, "商户费率"));
        list.add(new Cell(rowIdx, cellIdx++, "商户手续费（元）"));
        if (level.equals(UserLevel.ADMIN)) {
            list.add(new Cell(rowIdx, cellIdx++, "SP 手续费（元）"));
        }
        list.add(new Cell(rowIdx, cellIdx++, "代理商费率"));
        list.add(new Cell(rowIdx, cellIdx++, "代理商分润（元）"));
        if (level.equals(UserLevel.ADMIN)) {
            list.add(new Cell(rowIdx, cellIdx++, "纯利润（元）"));
        }

        // init.content
        List<List<String>> contentList = new ArrayList<>();
        List<Integer> idxList = Arrays.asList(0, 1, 2, 5, 6, 7, 8, 9, 11, 12);
        if (level.equals(UserLevel.ADMIN)) {
            contentList = detailList;
        } else {

            for (List<String> p : detailList) {

                List<String> q = new ArrayList<>();
                for (int i : idxList) {
                    q.add(p.get(i));
                }

                contentList.add(q);
            }
        }

        // fill.content
        excelService.fill(list, contentList, rowIdx);


        if (loginUser.getLevel().equals(UserLevel.ADMIN)) {
            // init.withdraw
            rowIdx = detailList.size() + 3;
            cellIdx = 0;

            // init.title.withdraw
            list.add(new Cell(rowIdx, cellIdx++, "日期"));
            list.add(new Cell(rowIdx, cellIdx++, "提现类型"));
            list.add(new Cell(rowIdx, cellIdx++, "提现笔数"));
            list.add(new Cell(rowIdx, cellIdx++, "手续费总收入（元）"));

            // init.content.withdraw
            contentList = calcWithdrawView(null, from, to);

            excelService.fill(list, contentList, rowIdx);
        }

        return excelService.fill("", list);
    }

    // Calc

    private List<List<String>> calcOrderView(ModelAndView view, Long cpId, String from, String to, Integer channel) {

        // init
        List<List<String>> dataList = new ArrayList<>();
        List<String> datas = new ArrayList<>();
        List<OrderVo> group = null;
        Integer count = 0;
        Double amountSum = 0d;
        Double feeSum = 0d;

        // query
        List<Order> list = orderService.statementBy(cpId, from, to, channel);
        if (CollectionUtils.isNotEmpty(list)) {

            // init
            Map<String, OrderVo> map = new HashMap<>();

            // foreach
            for (Order p : list) {

                // init
                String finishTime = DateUtils.FORMAT_YYYY_MM_DD.format(p.getFinishTime());
                Byte channelType = p.getChannelType();
                Double fee = Double.valueOf((null == p.getFee() ? 0 : p.getFee()) / 100.0f);
                double feeRecharge = p.getFeeRecharge().doubleValue();
                double amount = Double.valueOf((null == p.getAmount() ? 0 : p.getAmount()) / 100.0f);
                String key = String.format("%s_%s_%s", finishTime, channelType, feeRecharge);

                if (map.containsKey(key)) {

                    OrderVo q = map.get(key);
                    q.setCount(q.getCount() + 1);
                    q.setAmount(q.getAmount() + amount);
                    q.setFee(q.getFee() + fee);
                } else {

                    OrderVo q = new OrderVo();
                    BeanUtils.copyProperties(p, q);
                    q.setChannelName(OrderChannelEnum.getInfo(channelType));
                    q.setCount(1);
                    q.setAmount(amount);
                    q.setFee(fee);

                    map.put(key, q);
                }

                // sum
                ++count;
                amountSum += amount;
                feeSum += fee;
            }

            // group
            group = new ArrayList<>(map.values());

            // foreach
            for (OrderVo p : group) {

                // init
                datas = new ArrayList<>();

                // convert
                datas.add(SiteHelper.getSimpleTimeFormat(p.getFinishTime()));
                datas.add(p.getChannelName());
                datas.add(p.getCount().toString());
                datas.add(SiteHelper.formatMoneySimple(p.getAmount()));
                datas.add(Double.valueOf(p.getFeeRecharge().doubleValue() * 100) + "%");
                datas.add(SiteHelper.formatMoneySimple(p.getFee()));

                // add
                dataList.add(datas);
            }
        }

        // fill.view
        if (null != view) {
            view.addObject("count", count);
            view.addObject("amount", amountSum);
            view.addObject("fee", feeSum);
            view.addObject("lst", dataList);
        }

        return dataList;
    }

    private List<List<String>> calcSpView(ModelAndView view, String from, String to, Long spId, String spName, String service, Integer
            channel) throws ParseException {

        // init
        List<List<String>> datas = new ArrayList<>();
        List<OrderSpStatementVo> group = new ArrayList<>();
        double amountSum = 0d;
        double spFeeSum = 0d;

        // fill
        List<OrderSpStatementVo> list = orderService.statementSpBy(from, to, spId, spName, service, channel);
        if (CollectionUtils.isNotEmpty(list)) {

            // init
            Map<String, OrderSpStatementVo> map = new HashMap<>();

            // foreach
            for (OrderSpStatementVo p : list) {

                // init
                Long curSpId = p.getSpId();
                String curService = p.getService();
                Byte channelType = p.getChannelType();
                Double amount = (double) (null == p.getAmount() ? 0 : p.getAmount());
                Double spFee = (double) (null == p.getSpFee() ? 0 : p.getSpFee());
                String key = String.format("%s_%s_%s", curService, curSpId, channelType);

                // fill
                if (map.containsKey(key)) {

                    OrderSpStatementVo q = map.get(key);
                    q.setCount(q.getCount() + 1);
                    q.setAmount((double) (q.getAmount()) + amount);
                    q.setSpFee((double) (q.getSpFee()) + spFee);
                } else {

                    OrderSpStatementVo q = new OrderSpStatementVo();
                    BeanUtils.copyProperties(p, q);
                    q.setChannelName(OrderChannelEnum.getInfo(channelType));
                    q.setCount(1);
                    q.setAmount(amount);
                    q.setSpFee(spFee);

                    map.put(key, q);
                }
            }

            // group
            group = new ArrayList<>(map.values());
        }

        // foreach
        if (CollectionUtils.isNotEmpty(group)) {

            // order
            Collections.sort(group, new Comparator<OrderSpStatementVo>() {
                @Override
                public int compare(OrderSpStatementVo o1, OrderSpStatementVo o2) {

                    if (o1.getBank().trim().equalsIgnoreCase(o2.getBank().trim())) {
                        if (o1.getSpName().trim().equalsIgnoreCase(o2.getSpName().trim())) {
                            return 0;
                        } else {
                            return o1.getSpName().trim().compareToIgnoreCase(o2.getSpName().trim());
                        }
                    } else {
                        return o1.getBank().trim().compareToIgnoreCase(o2.getBank().trim());
                    }
                }
            });

            // init
            Map<Long, Sp> map = spService.findByIdIn(group.stream().map(p -> p.getSpId()).distinct().collect(Collectors.toList())).stream().collect(Collectors.toMap(p -> p.getId(), p -> p));

            // fill
            for (
                    OrderSpStatementVo p : group)

            {

                List<String> data = new ArrayList<>();
                Sp sp = map.get(p.getSpId());

                data.add(sp.getId().toString());
                data.add(sp.getBank());
                data.add(sp.getMchId());
                data.add(sp.getName());
                data.add(OrderChannelEnum.getInfo(p.getChannelType()));
                data.add(p.getCount().toString());
                data.add(SiteHelper.formatMoneySimple(p.getAmount(), 100.0));
                data.add(SiteHelper.formatPercentage(p.getSpFeeRecharge()));
                data.add(SiteHelper.formatMoneySimple(p.getSpFee(), 100.0));
                data.add(SiteHelper.formatMoneySimple(p.getAmount() - p.getSpFee(), 100.0));
                data.add(sp.getCardNo());

                amountSum += (null != p.getAmount() ? p.getAmount() : 0d);
                spFeeSum += (null != p.getSpFee() ? p.getSpFee() : 0d);

                datas.add(data);
            }
        }

        if (null != view)

        {
            view.addObject("amount", amountSum);
            view.addObject("spFee", spFeeSum);
            view.addObject("left", amountSum - spFeeSum);
        }

        return datas;
    }

    private List<List<String>> calcAllView(ModelAndView view, String from, String to, Long cpId, String
            cpName, Long agentId, String contract, Integer channel) {

        // init
        List<List<String>> datas = new ArrayList<>();
        List<OrderStatementVo> group = new ArrayList<>();
        Integer count = 0;
        Double amountSum = 0d;
        Double feeSum = 0d;
        Double spFeeSum = 0d;
        Double agentProfitSum = 0d;
        Double profitSum = 0d;

        /**
         * 「query」
         * 成功总金额  amount
         * 商户费率    fee_recharge
         * 商户手续费  fee
         * SP手续费    sp_fee
         * 代理商费率  agent_fee_recharge
         * 代理商分润  agent_fee
         * 纯利润      fee- sp_fee- agent_fee
         */
        List<OrderStatementVo> list = orderService.statementAllBy(from, to, cpId, cpName, agentId, contract, channel);
        if (CollectionUtils.isNotEmpty(list)) {

            // init
            Map<String, OrderStatementVo> map = new HashMap<>();

            // foreach
            for (OrderStatementVo p : list) {

                // init
                String finishTime = DateUtils.FORMAT_YYYY_MM_DD.format(p.getFinishTime());
                Byte channelType = p.getChannelType();
                Long curCpId = p.getCpId();
                double feeRecharge = p.getFeeRecharge().doubleValue();
                double spFeeRecharge = p.getSpFeeRecharge().doubleValue();
                Double amount = p.getAmount();
                Double fee = p.getFee() != null ? p.getFee() : 0;
                Double spFee = p.getSpFee() != null ? p.getSpFee() : 0;
                Double agentFee = p.getAgentFee() != null ? p.getAgentFee() : 0;
                Double agentProfit = 0 == agentFee ? 0 : (fee - agentFee);
                Double profit = 0 == agentFee ? (fee - spFee) : (agentFee - spFee);
                String key = String.format("%s_%s_%s_%s_%s", curCpId, channelType, feeRecharge, spFeeRecharge, finishTime);

                // fill
                if (map.containsKey(key)) {

                    OrderStatementVo q = map.get(key);
                    q.setCount(q.getCount() + 1);
                    q.setAmount(q.getAmount() + amount);
                    q.setFee(q.getFee() + fee);
                    q.setSpFee(q.getSpFee() + spFee);
                    q.setAgentProfit(q.getAgentProfit() + agentProfit);
                    q.setProfit(q.getProfit() + profit);
                } else {

                    OrderStatementVo q = new OrderStatementVo();
                    BeanUtils.copyProperties(p, q);
                    q.setChannelName(OrderChannelEnum.getInfo(channelType));
                    q.setCount(1);
                    q.setProfit(profit);
                    q.setFee(fee);
                    q.setSpFee(spFee);
                    q.setAgentProfit(agentProfit);
                    q.setProfit(profit);
                    map.put(key, q);
                }

                // sum
                ++count;
                amountSum += amount;
                feeSum += fee;
                spFeeSum += spFee;
                agentProfitSum += agentProfit;
                profitSum += profit;
            }

            // group
            group = new ArrayList<>(map.values());
        }

        // convert
        if (CollectionUtils.isNotEmpty(group)) {

            // order
            Collections.sort(group, new Comparator<OrderStatementVo>() {
                @Override
                public int compare(OrderStatementVo o1, OrderStatementVo o2) {

                    String val1 = format.format(o1.getFinishTime()) + o1.getCpId().toString() + o1.getChannelType().toString() + o1.getFeeRecharge().toString() + o1.getSpFeeRecharge().toString() + (null == o1.getAgentFeeRecharge() ? 0 : o1.getAgentFeeRecharge().toString().toString());
                    String val2 = format.format(o2.getFinishTime()) + o2.getCpId().toString() + o2.getChannelType().toString() + o2.getFeeRecharge().toString() + o2.getSpFeeRecharge().toString() + (null == o2.getAgentFeeRecharge() ? 0 : o2.getAgentFeeRecharge().toString().toString());
                    return val1.compareToIgnoreCase(val2);
                }
            });

            // fill
            for (
                    OrderStatementVo p : group)

            {

                List<String> q = new ArrayList<>();

                q.add(SiteHelper.getSimpleTimeFormat(p.getFinishTime()));
                q.add(p.getCpId().toString());
                q.add(p.getCpName());
                q.add(SiteHelper.getEmptyStr(p.getAgentId(), "0"));
                q.add(SiteHelper.getEmptyStr(p.getAgentName(), "无"));
                q.add(p.getChannelName());
                q.add(p.getCount().toString());
                q.add(SiteHelper.formatMoneySimple(p.getAmount(), 100.0));
                q.add(SiteHelper.formatPercentage(p.getFeeRecharge()));
                q.add(SiteHelper.formatMoneySimple(p.getFee(), 100.0));
                q.add(SiteHelper.formatMoneySimple(p.getSpFee(), 100.0));
                q.add(SiteHelper.formatPercentage(p.getAgentFeeRecharge()));
                q.add(SiteHelper.formatMoneySimple(p.getAgentProfit(), 100.0));
                q.add(SiteHelper.formatMoneySimple(p.getProfit(), 100.0));

                datas.add(q);
            }
        }

        if (null != view) {
            view.addObject("count", count);
            view.addObject("amount", amountSum);
            view.addObject("fee", feeSum);
            view.addObject("spFee", spFeeSum);
            view.addObject("agentProfit", agentProfitSum);
            view.addObject("profit", profitSum);
        }

        return datas;
    }

    private List<List<String>> calcWithdrawView(ModelAndView view, String from, String to) {

        // init
        List<List<String>> list = new ArrayList<>();
        List<WithdrawResultVo> group = new ArrayList<>();
        Map<String, WithdrawResultVo> map = new HashMap<>();
        Double feesum=0d;
        int countsum=0;

        // query
        List<CpWithdrawTradlog> tradlogs = withdrawService.list(from, to);
        if (CollectionUtils.isNotEmpty(tradlogs)) {
            // filter
            tradlogs = tradlogs.stream().filter(p -> p.getState().equals(WithdrawStateEnum.PAYED.getState())).collect(Collectors.toList());

            if (CollectionUtils.isNotEmpty(tradlogs)) {

                WithdrawResultVo q = null;
                for (CpWithdrawTradlog p : tradlogs) {
                    int count=p.getAmount()!=null?p.getAmount():0;
                    Double fee=p.getFee()!=null?p.getFee().doubleValue():0;

                    // init
                    Date finishTime = p.getFinishTime();
                    if (null == finishTime || null == p.getType())
                        continue;
                    String date = format.format(finishTime);
                    String key = String.format("%s_%s", count,fee);

                    // fill.data
                    if (map.containsKey(key)) {
                        q = map.get(key);
                        q.setCount(q.getCount() + 1);
                        q.setFee(q.getFee() + fee);
                    } else {
                        q = new WithdrawResultVo(date, p.getType(), p.getFee().doubleValue(),
                                new BigDecimal(String.valueOf(count)).divide(new BigDecimal("100")).doubleValue());
                    }
                    // fill.map
                    map.put(key, q);
                    feesum+=fee;
                    countsum++;
                }

                group = new ArrayList<>(map.values());
            }
        }

        // convert
        if (CollectionUtils.isNotEmpty(group)) {
            // order
            Collections.sort(group, new Comparator<WithdrawResultVo>() {
                @Override
                public int compare(WithdrawResultVo o1, WithdrawResultVo o2) {
                    String val1 = o1.getDate() + o1.getType().toString();
                    String val2 = o2.getDate() + o2.getType().toString();
                    return val1.compareToIgnoreCase(val2);
                }
            });
            // for
            for (WithdrawResultVo p : group) {
                // init
                List<String> q = new ArrayList<>();
                // fill
                q.add(p.getDate());
                q.add(WithdrawTypeEnum.getInfo(p.getType()));
                q.add(String.valueOf(p.getAmount()));
                q.add(String.valueOf(p.getCount()));
                q.add(SiteHelper.formatMoneySimple(p.getFee(), 100.0));
                list.add(q);
            }
        }
        if (null != view) {
            view.addObject("feesum", feesum);
            view.addObject("countsum", countsum);
        }
        return list;
    }

    // Commons

    private void fillCpList(ModelAndView view, UserLevel level, UserDetailsServiceImpl.LoginUserDetails loginUser) {

        // check
        Assert.notNull(view);
        Assert.notNull(level);

        // init
        Map<Long, String> map = null;

        // query
        if (level == UserLevel.ADMIN) {

            List<CpInfo> list = cpInfoService.listAll();
            if (CollectionUtils.isNotEmpty(list)) {

                map = list.stream().collect(Collectors.toMap(p -> p.getCpId(), p -> p.getName()));
            }
        } else if (level == UserLevel.AGENT) {

            Long cpId = Long.valueOf(loginUser.getUsername());
            List<CpInfo> list = cpInfoService.listByAgent(cpId);
            if (CollectionUtils.isNotEmpty(list)) {
                map = new HashMap<>(list.size());
                map.putAll(list.stream().collect(Collectors.toMap(p -> p.getCpId(), p -> p.getName())));
            }
        } else {

            Long cpId = Long.valueOf(loginUser.getUsername());
            CpInfo info = cpInfoService.get(cpId);
            map = new HashMap<>(1);
            map.put(cpId, null != info ? info.getName() : cpId.toString());
        }

        // fill
        view.addObject("cpMap", map);
    }

    private void fillCp(Object... params) throws ParseException {

        // check
        Assert.isTrue(ArrayUtils.isNotEmpty(params) && params.length == 6, "");

        // parse
        List<String> labels = (List<String>) params[0];
        List<DataSetVo> datas = (List<DataSetVo>) params[1];
        List<Long> cpIdList = (List<Long>) params[2];
        String from = (String) params[3];
        String to = (String) params[4];
        List<CpAccountTradlog> tradLogs = (List<CpAccountTradlog>) params[5];

        // init
        Map<Long, Map<String, Float>> map = null;
        List<Double> data = null;

        // check
        if (StringUtils.isBlank(from) || StringUtils.isBlank(to) || CollectionUtils.isEmpty(cpIdList))
            return;

        // label
        fillLabel(labels, from, to);

        // data.calc
        map = new HashMap<>();
        Map<String, Float> calcMap = null;
        if (CollectionUtils.isNotEmpty(tradLogs))
            for (CpAccountTradlog p : tradLogs) {

                // init
                Long cpId = p.getCpId();
                String key = DateUtils.FORMAT_YYYY_MM_DD.format(p.getCreateTime());
                Float amount = p.getAmount() / 100.0f;

                // calc
                if (!map.containsKey(cpId)) {
                    map.put(cpId, new HashMap<String, Float>());
                }
                calcMap = map.get(cpId);
                if (calcMap.containsKey(key)) {
                    calcMap.put(key, amount + calcMap.get(key));
                } else {
                    calcMap.put(key, amount);
                }
            }

        // data.fill
        for (Long cpId : map.keySet()) {

            // init
            data = new ArrayList<>();
            String cpName = "";

            // name
            CpInfo info = cpInfoService.get(cpId);
            cpName = null != info ? info.getName() : cpId.toString();

            // fill
            calcMap = map.get(cpId);
            for (String p : labels) {
                data.add(calcMap.containsKey(p) ? Double.valueOf(calcMap.get(p)) : 0d);
            }
            datas.add(new DataSetVo(cpName, data));
        }
    }

    private void fill(Object... params) throws ParseException {

        // check
        Assert.isTrue(ArrayUtils.isNotEmpty(params) && params.length == 5, "");

        // parse
        List<String> labels = (List<String>) params[0];
        List<DataSetVo> datas = (List<DataSetVo>) params[1];
        String from = (String) params[2];
        String to = (String) params[3];
        List<QipayTradlog> tradLogs = (List<QipayTradlog>) params[4];

        // init
        Map<String, Float> calcMap = null;
        List<Double> data = null;

        // check
        if (StringUtils.isBlank(from) || StringUtils.isBlank(to))
            return;

        // label
        fillLabel(labels, from, to);

        // data.calc
        calcMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(tradLogs))
            for (QipayTradlog p : tradLogs) {
                String key = DateUtils.FORMAT_YYYY_MM_DD.format(p.getCreateTime());
                if (calcMap.containsKey(key)) {
                    calcMap.put(key, p.getQipayAmount() / 100.0f + calcMap.get(key));
                } else {
                    calcMap.put(key, p.getQipayAmount() / 100.0f);
                }
            }

        // data.fill
        data = new ArrayList<>();
        for (String p : labels) {
            data.add(calcMap.containsKey(p) ? Double.valueOf(calcMap.get(p)) : 0d);
        }
        datas.add(new DataSetVo("QiPay", data));
    }

    private void fillLabel(List<String> labels, String from, String to) throws ParseException {

        // check
        Assert.isTrue(StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to));

        // init
        Calendar cFrom = Calendar.getInstance();
        cFrom.setTime(DateUtils.FORMAT_YYYY_MM_DD.parse(from));
        Calendar cTo = Calendar.getInstance();
        cTo.setTime(DateUtils.FORMAT_YYYY_MM_DD.parse(to));

        // fill
        for (; cFrom.get(Calendar.YEAR) != cTo.get(Calendar.YEAR) || cFrom.get(Calendar.DAY_OF_YEAR) != cTo.get(Calendar.DAY_OF_YEAR); cFrom.add(Calendar.DAY_OF_YEAR, 1)) {
            labels.add(DateUtils.FORMAT_YYYY_MM_DD.format(cFrom.getTime()));
        }
    }


//    private List<List<String>> calcAllView2(ModelAndView view, String from, String to, Long cpId, String
//            cpName, Long agentId, String contract, Integer channel) {
//
//        // init
//        List<List<String>> datas = new ArrayList<>();
//        List<OrderStatementVo> group = new ArrayList<>();
//        Integer count = 0;
//        Double amountSum = 0d;
//        Double feeSum = 0d;
//        Double spFeeSum = 0d;
//        Double agentProfitSum = 0d;
//        Double profitSum = 0d;
//        return datas;
//    }

}
